<?php

use app\common\service\ManagerService;
use app\common\service\permission\AdminService;
use app\common\service\user\UserService;
use app\common\util\JwtUtil;
use Channel\Server as ChannelServer;
use think\App;
use think\Container;
use think\Db;
use think\facade\Cache;
use think\facade\Log;
use Workerman\Connection\TcpConnection;
use Workerman\Timer;
use Workerman\Worker;

require_once __DIR__ . '/vendor/autoload.php';

// 超时时间（秒）
const HEARTBEAT_TIMEOUT = 55;

$thinkApp = new App();
$thinkApp->initialize();

// 启动独立的 Channel 服务器（必须）
$channel_server = new ChannelServer('0.0.0.0', 2206);

$ws_worker = new Worker("websocket://0.0.0.0:2345");

// 启动4个进程对外提供服务
$ws_worker->count = 4;

$redis = Cache::store('redis')->handler();

// 每个Worker进程独立初始化
$ws_worker->onWorkerStart = function($worker) use ($thinkApp) {
    // 克隆主应用实例到子进程
    $app = clone $thinkApp;

    // 手动重建连接
    $db = app()->make(Db::class);
    $db->connect();

    // 注册当前进程的应用实例
    Container::setInstance($app);

    // 连接到 Channel 服务器（协程安全）
    \Channel\Client::connect('127.0.0.1', 2206);
    // 订阅跨进程消息频道
    \Channel\Client::on('send_to_user', function ($data) use ($worker) {
        foreach ($worker->connections as $conn) {
            if (isset($conn->uid) && $conn->uid == $data['to_uid']) {
                $conn->send(json_encode($data['message']));
                Log::info("跨进程投递消息至用户 {$data['to_uid']} (进程 {$worker->id})");
            }
        }
    });

    Timer::add(10, function()use($worker){
        $time_now = time();
        foreach($worker->connections as $connection) {
            // 有可能该connection还没收到过消息，则lastMessageTime设置为当前时间
            if (empty($connection->lastMessageTime)) {
                $connection->lastMessageTime = $time_now;
                continue;
            }
            // 上次通讯时间间隔大于心跳间隔，则认为客户端已经下线，关闭连接
            if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIMEOUT) {
                Log::error("{$connection->id} . 断开");
                $connection->close();
            }
        }
    });
};

// WebSocket 握手完成后触发
$ws_worker->onWebSocketConnect = function (TcpConnection $conn, $http_request) {
    // 检查 $http_request 是否存在
    if (!$http_request) {
        echo "HTTP 请求对象不存在，无法解析参数。\n";
        return;
    }

    // 从 HTTP 请求对象中解析 URI 和查询参数
    $uri = $http_request->uri(); // 获取完整的 URL
    $query = parse_url($uri, PHP_URL_QUERY); // 提取查询字符串部分
    parse_str($query, $params); // 将查询字符串解析为关联数组

    // 输出调试信息
    echo "解析到的查询参数: " . json_encode($params) . "\n";

    // 检查并获取 uid 参数
    if ($uid = $params['uid'] ?? 0) {
        $conn->uid = $uid; // 将 uid 存储到连接对象中
        echo "用户 UID: $uid 已绑定到连接。\n";

        // 记录用户-进程映射到 Redis
        Cache::store('redis')->handler()->hSet('user_process_map', $uid, $conn->worker->id);
        echo $conn->worker->id;
        echo "用户 $uid 的进程 ID 已记录到 Redis。\n";
    } else {
        echo "未找到 uid 参数，无法绑定用户。\n";
    }
};

// 初始化连接
$ws_worker->onConnect = function(TcpConnection $connection) use ($redis)
{
    $connection->send(json_encode([
        'code'    => 200,
        'method'  => 'CONNECT',
        'msg'     => '连接成功',
        'version' => '1.0',
    ]));
};

// 当收到客户端发来的数据
$ws_worker->onMessage = function(TcpConnection $connection, $data)
{
    try {
        // 给connection临时设置一个lastMessageTime属性，用来记录上次收到消息的时间
        $connection->lastMessageTime = time();

        $data = json_decode($data, true);

        // 1. 基础数据校验
        if (!isset($data['method']) || !isset($data['data'])) {
            return $connection->send(json_encode([
                'code' => 400,
                'msg' => '参数错误'
            ]));
        }

        // 2. 身份验证
        if($data['method'] === 'LOGIN') {
            // JWT 验证
            $payload = JwtUtil::validateToken($data['data']['login_token']);
            Log::write(print_r($payload, true), 'info');
            if (!$payload) {
                return $connection->send(json_encode([
                    'code' => 401,
                    'msg'  => 'Token 无效或已过期'
                ]));
            }

            // 用户状态验证
            $userId = $payload['user_id'];
            $role = $payload['role'];

            if($role === 'user') {
                $userInfo = Cache::get('user:info:'.$userId);
                if (!$userInfo) {
                    $userService = app()->make(UserService::class);
                    $userInfo = $userService->getInfo($userId);
                    if(!$userInfo) {
                        return $connection->send(json_encode([
                            'code' => 403,
                            'msg'  => '用户已被禁用'
                        ]));
                    }
                    Cache::set('user:info:'.$userId, $userInfo, 3600);
                }
            } elseif($role === 'admin') {
               $userInfo = Cache::get('admin:info:'.$userId);
               if(!$userInfo) {
                   $adminService = app()->make(AdminService::class);
                   $userInfo = $adminService->getAdminInfo($userId);
                   if(!$userInfo) {
                       return $connection->send(json_encode([
                           'code' => 403,
                           'msg'  => '用户已被禁用'
                       ]));
                   }
                   Cache::set('admin:info:'.$userId, $userInfo, 3600);
               }
            } else {
                return $connection->send(json_encode([
                    'code' => 401,
                    'msg'  => '认证失败'
                ]));
            }


            // 绑定用户连接
            ManagerService::bindUser($connection, $userId, $role);

            // 登录成功返回
            return $connection->send(json_encode([
                'code' => 200,
                'method' => 'PING',
                'msg' => '连接成功',
                'params' => [
                    'client_id' => $userId,
                    'username'  => $userInfo['username']
                ]
            ]));
        }

        // 3. 已认证消息处理
        if (!isset($connection->uid)) {
            return $connection->send(json_encode([
                'code' => 401,
                'msg'  => '请先进行身份认证'
            ]));
        }

        // 4. 业务消息路由
        switch ($data['method']) {
            case 'CS_REPLY':
                ManagerService::handleCsReply($connection, $data['data']);
                break;
            case 'CHAT':
                // 处理聊天消息
                ManagerService::handleChatMessage($connection, $data['data']);
                break;
            case 'RECALL_MSG':
                // 消息撤回
                ManagerService::recallMsg($connection, $data['data']);
                break;
            case 'FRIEND':
                // 喜欢|不喜欢
                ManagerService::handlePreference($connection, $data['data']);
                break;
            case 'PING':
                // 心跳检测
                $connection->send(json_encode([
                    'code' => 200,
                    'method' => 'PUSH',
                    'msg' => '心跳',
                    'params' => [
                        'action' => 'ping',
                    ]
                ]));
                break;
            default:
                $connection->send(json_encode([
                    'code' => 404,
                    'msg'  => '未知消息类型'
                ]));
        }
    } catch (Throwable $e) {
        // 记录日志
        Log::error('WS处理异常: '.$e->getMessage());
        return $connection->send(json_encode(['code' => 500, 'message' => '内部错误']));
    }
    return false;
};

// 连接关闭时清理
$ws_worker->onClose = function(TcpConnection $connection) {
    ManagerService::removeConnection($connection);
};

// 运行worker
Worker::runAll();